﻿using System;
using System.Collections.Generic;
using System.Text;
using System.IO;
using System.Runtime.Serialization.Formatters.Binary;
using System.Xml;
using System.Xml.Serialization;

namespace WebChatApi.Common
{
    /// <summary>
    /// Class SerializeUtility. This class cannot be inherited.
    /// </summary>
    public sealed class SerializeUtility : Singleton<SerializeUtility>
    {
        /// <summary>
        /// The formatter
        /// </summary>
        private static BinaryFormatter formatter = new BinaryFormatter();

        /// <summary>
        /// Initializes a new instance of the <see cref="SerializeUtility" /> class.
        /// </summary>
        public SerializeUtility() { }

        /// <summary>
        /// Serializes to stream.
        /// </summary>
        /// <param name="objectToSerialize">The object to serialize.</param>
        /// <returns>MemoryStream.</returns>
        public MemoryStream SerializeToStream(object objectToSerialize)
        {
            this.CheckNullObject(objectToSerialize, "objectToSerialize");

            MemoryStream memoryStream = new MemoryStream();
            try
            {
                formatter.Serialize(memoryStream, objectToSerialize);
                return memoryStream;
            }
            finally
            {
                memoryStream.Close();
            }
        }

        /// <summary>
        /// Serializes to byte array.
        /// </summary>
        /// <param name="objectToSerialize">The object to serialize.</param>
        /// <returns>System.Byte[][].</returns>
        public byte[] SerializeToByteArray(object objectToSerialize)
        {
            return SerializeToStream(objectToSerialize).ToArray();
        }

        /// <summary>
        /// Serializes to base64 string.
        /// </summary>
        /// <param name="objectToSerialize">The object to serialize.</param>
        /// <returns>System.String.</returns>
        public string SerializeToBase64String(object objectToSerialize)
        {
            this.CheckNullObject(objectToSerialize, "objectToSerialize");

            byte[] byteArray = SerializeToByteArray(objectToSerialize);
            return Convert.ToBase64String(byteArray, 0, byteArray.Length);
        }


        /// <summary>
        /// Serializes to XML.
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="objectToSerialize">The object to serialize.</param>
        /// <param name="encoding">The encoding.</param>
        /// <param name="omitXmlDeclaration">if set to <c>true</c> [omit XML declaration].</param>
        /// <param name="indentedXml">if set to <c>true</c> [indented XML].</param>
        /// <returns>System.String.</returns>
        public string SerializeToXml<T>(T objectToSerialize, Encoding encoding, bool omitXmlDeclaration = true, bool indentedXml = false)
        {
            this.CheckNullObject(objectToSerialize, "objectToSerialize");

            MemoryStream stream = null;

            try
            {
                XmlSerializer serializer = new XmlSerializer(objectToSerialize.GetType());
                stream = new MemoryStream();
                var writer = XmlWriter.Create(stream, new XmlWriterSettings { Encoding = encoding, OmitXmlDeclaration = omitXmlDeclaration, Indent = indentedXml });

                serializer.Serialize(writer, objectToSerialize);
                string result = encoding.GetString(stream.ToArray());
                writer.Close();
                return result;
            }
            finally
            {
                if (stream != null)
                {
                    stream.Close();
                }
            }
        }

        /// <summary>
        /// Serializes to XML.
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="objectToSerialize">The object to serialize.</param>
        /// <returns>System.String.</returns>
        public string SerializeToXml<T>(T objectToSerialize)
        {
            return SerializeToXml<T>(objectToSerialize, Encoding.UTF8);
        }

        /// <summary>
        /// Deserializes from stream.
        /// </summary>
        /// <param name="stream">The stream.</param>
        /// <returns>System.Object.</returns>
        public object DeserializeFromStream(MemoryStream stream)
        {
            this.CheckNullObject(stream, "stream");
            object result = null;

            try
            {
                result = formatter.Deserialize(stream);
            }
            finally
            {
                stream.Close();
            }

            return result;
        }

        /// <summary>
        /// Deserializes from byte array.
        /// </summary>
        /// <param name="byteArray">The byte array.</param>
        /// <returns>System.Object.</returns>
        public object DeserializeFromByteArray(byte[] byteArray)
        {
            this.CheckNullObject(byteArray, "byteArray");
            MemoryStream memoryStream = new MemoryStream(byteArray, 0, byteArray.Length);
            return DeserializeFromStream(memoryStream);
        }

        /// <summary>
        /// Deserializes from base64 string.
        /// </summary>
        /// <param name="base64String">The base64 string.</param>
        /// <returns>System.Object.</returns>
        public object DeserializeFromBase64String(string base64String)
        {
            this.CheckNullObject(base64String, "base64String");

            byte[] byteArray = Convert.FromBase64String(base64String);
            return DeserializeFromByteArray(byteArray);
        }

        /// <summary>
        /// Deserializes from XML.
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="xml">The XML.</param>
        /// <param name="encoding">The encoding.</param>
        /// <returns>``0.</returns>
        public T DeserializeFromXml<T>(string xml, Encoding encoding)
        {
            this.CheckNullObject(xml, "xml");
            XmlSerializer serializer = new XmlSerializer(typeof(T));
            StreamReader memoryStream = new StreamReader(new MemoryStream(encoding.GetBytes(xml)));

            try
            {
                return (T)serializer.Deserialize(memoryStream);
            }
            finally
            {
                memoryStream.Close();
            }
        }

        /// <summary>
        /// Deserializes from XML.
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="xml">The XML.</param>
        /// <returns>``0.</returns>
        public T DeserializeFromXml<T>(string xml)
        {
            return DeserializeFromXml<T>(xml, Encoding.UTF8);
        }
    }
}
